#!/bin/bash

###################################################
# input (enviromental variable):
# * OS_VER        -   ubuntu-14.04 or ubuntu-16.04, by default is ubuntu-14.04
# * BUILD_TYPE    -   vmware or virtualbox, by default is virtualbox
# * ANSIBLE_PLAYBOOK - which is the ansible yml playbook used. options are rackhd_package or rackhd_local, default is rackhd_local.
# * RACKHD_VERSION        - optional , by default is blank(will apt-get install latest rackhwill apt-get install latest rackhd). used for rackhd_package.yml to install specific version of rackhd.deb(refer to https://bintray.com/rackhd to find correct version you need)
# * DEBIAN_REPOSITORY - optional, by defaul is to using RackHD Bintray repo: "deb https://dl.bintray.com/rackhd/debian trusty release", if using latest nightly build, can choose "deb https://dl.bintray.com/rackhd/debian trusty main". and other repo like private artifactory is also ok. just let the /etc/apt/source.list recognize it.
# * UPLOAD_BOX_TO_ATLAS   - optional, by default is false. tell packer if upload the vagrant box generated to ATLAS. NOTE: ATLAS user/token and target ATLAS repo name should be specificed.
# * ATLAS_USERNAME - optional, required only when UPLOAD_BOX_TO_ATLAS is true.
# * ATLAS_NAME     - optional, required only when UPLOAD_BOX_TO_ATLAS is true.
# * ATLAS_TOKEN    - optional, required only when UPLOAD_BOX_TO_ATLAS is true.
# * ATLAS_VERSION  - optional, by default is "".when UPLOAD_BOX_TO_ATLAS is true, the box will be uploaded to ATLAS as ATLAS_VERSION( format only accepts like 1.2.3).
# * CUSTOMIZED_PROPERTY_OVA - optional, by default false, only effective when BUILD_TYPE is vmware.. 
#                           - if true, the OVA can be specified IP during deployment. only supported by vCenter
# * BUILD_STAGE     - optional, by default is BUILD_ALL. other options as below:
#                   - "BUILD_TEMPLATE" (a prebuild cache, vmx or ovf),
#                   - "BUILD_FINAL"( Build ova or box from the template) , or "BUILD_ALL"(including both steps).
###################################################

INFO_HEADER="[Info]"
WARNING_HEADER="[Warning]"
ERROR_HEADER="[Error]"


# early exit on command failure
set -e

echo ${INFO_HEADER}" Please use ENV VAR to pass customized parameter to this script. Please refer to the usage on the top of script file."

############################  Parameter Process ################ start #############

####  Ubuntu Version  ###
if  [ ! -n "${OS_VER}" ];  then
    OS_VER=ubuntu-14.04
fi
echo "${INFO_HEADER} Packer Build based on $OS_VER ( using template-${OS_VER}.json)"

#### Build Vagrant or VMware OVA ? ###
if  [ ! -n "${BUILD_TYPE}" ];  then
    BUILD_TYPE=virtualbox  # option is vmware or vitrualbox , case senstive
fi
if [[ ${BUILD_TYPE} != "virtualbox" ]] && [[ ${BUILD_TYPE} != "vmware" ]] ; then
    echo "${ERROR_HEADER} Bad Env Var: the BUILD_TYPE=$BUILD_TYPE is invalid, it should be either virtualbox or vmware.. "
    exit 2
fi

echo "${INFO_HEADER} Packer Build : ${BUILD_TYPE}"

#### if upload vagrant box to ATLAS ###
IS_UPLOAD_ATLAS=false
if [[ ${UPLOAD_BOX_TO_ATLAS,,} == "true" ]];
then
    IS_UPLOAD_ATLAS=true
fi
if [ $BUILD_TYPE != "virtualbox" ] && [ $IS_UPLOAD_ATLAS == true ]; then
    echo "${ERROR_HEADER} Bad Env Var: only when for Vagrant Box build. ( BUILD_TYPE=virtualbox), the $UPLOAD_BOX_TO_ATLAS can be set to true . Abort !"
    exit 2
fi

### Check the ATLAS accounts ###
if [ $IS_UPLOAD_ATLAS == true ]; then
   if [ ! -n "${ATLAS_USERNAME}" ] || [ ! -n "${ATLAS_NAME}" ] || [ ! -n "${ATLAS_TOKEN}" ];  then
       echo "${ERROR_HEADER} Bad Env Var: When ENV VAR \$UPLOAD_BOX_TO_ATLAS is true, the ATLAS_USERNAME/ATLAS_NAME/ATLAS_TOKEN should be given.  Abort !"
       exit 2
   fi
   if [ ! -n "${ATLAS_VERSION}" ];  then
       ATLAS_VERSION=""
   else
       if [[ $ATLAS_VERSION != +([0-9]).+([0-9]).+([0-9]) ]] ; then
           echo "${ERROR_HEADER}: ATLAS_VERSION(${ATLAS_VERSION}) should be X.Y.Z (X.Y.Z is digit), that's only format accepted by ATLAS."
          exit 2
      fi
   fi
fi


# By default. use ansible playbook : rackhd_local.yml
if  [ ! -n "${ANSIBLE_PLAYBOOK}" ];  then
    ANSIBLE_PLAYBOOK=rackhd_local
fi
if [ ! -f "ansible/${ANSIBLE_PLAYBOOK}.yml" ]; then
    echo "${ERROR_HEADER} The target ansible playbook(ansible/${ANSIBLE_PLAYBOOK}.yml) does not exist. Aborting..."
    exit 3
fi

### For OVA Build, option is to build OVA directlly from VMX, or build a VMX (which includes all RackHD Prerequisite)
if  [ ! -n "${BUILD_STAGE}" ];  then
    BUILD_STAGE="BUILD_ALL"
fi

if [ "$BUILD_STAGE" == "BUILD_ALL" ] ; then
    echo "${INFO_HEADER} Full Build:  Install RackHD with (1) ansible playbook rackhd_prepare.yml, (2) ansible playbook ${ANSIBLE_PLAYBOOK}_mini.yml "
else
    if [ "$BUILD_STAGE" == "BUILD_FINAL" ] ; then
        echo "${INFO_HEADER} Second Half Build: Install RackHD with ansible :  ${ANSIBLE_PLAYBOOK}_mini.yml "
    else
        if [ "$BUILD_STAGE" == "BUILD_TEMPLATE" ] ; then
            echo "${INFO_HEADER} First Half Build: Prepare RackHD dependency with ansible :  rackhd_prepare.yml "
        else
            echo "${ERROR_HEADER} Unrecongnized parameter BUILD_STAGE=$BUILD_STAGE,  Abort ! "
            exit 2
        fi
    fi
fi


### $RACKHD_VERSION is used for package install provisioner:  `apt-get install rackhd=$RACKHD_VERSION` ###

if [  -n "${RACKHD_VERSION}" ];  then
   if [ $ANSIBLE_PLAYBOOK != "rackhd_local" ]; then
       echo "${INFO_HEADER} Build VM based on RackHD Debian Package ${RACKHD_VERSION} "
  else
       echo "${WARNING_HEADER} Skip the ENV VAR: RACKHD_VERSION=$RACKHD_VERSION, this variable is useless for rackhd_local.yml (build from src code)".
   fi
else
   if [ $ANSIBLE_PLAYBOOK != "rackhd_local" ]; then
       echo "${INFO_HEADER} Install RackHD Package without specific version."
   fi
fi

### Choose a deb repository/component , according to IS_OFFICIAL_RELEASE parameter
if [ $ANSIBLE_PLAYBOOK != "rackhd_local" ]; then
      if [  -n "${DEBIAN_REPOSITORY}" ];  then
           echo "${INFO_HEADER} Build VM based on RackHD Debian Source Repository : using default one in ansible playbook"
      else
           echo "${INFO_HEADER} Build VM based on RackHD Debian Source Repository : $DEBIAN_REPOSITORY "
      fi
else
       if [  -n "${DEBIAN_REPOSITORY}" ];  then
           echo "${WARNING_HEADER} Skip the ENV VAR: DEBIAN_REPOSITORY=$DEBIAN_REPOSITORY, this variable is useless for rackhd_local.yml (build from src code)".
       fi
fi



# By default. disable CUSTOMIZED_PROPERTY_OVA, because only supported by vCenter. not by vSphere
if  [ ! -n "${CUSTOMIZED_PROPERTY_OVA}" ];  then
    CUSTOMIZED_PROPERTY_OVA=false;
fi

if [ "$BUILD_TYPE"  == "vmware" ]
then
    if [ "${CUSTOMIZED_PROPERTY_OVA}" == "true" ];
    then
         echo "${INFO_HEADER} the OVA created will carry with customized property, like admin port IP "
    else
         echo "${INFO_HEADER} the OVA created will not carry with customized property."
    fi
fi





############################  Parameter Process ################ end #############

# Enable Verbose Packer Logging
# see https://www.packer.io/docs/other/environmental-variables.html for details
export PACKER_LOG=1
export PACKER_LOG_PATH=./packer-debug.log
export PACKER_NO_COLOR=1  # for Jenkins usage. if manual run, suggest to turn color on (set to 0)




# default is output-${type}. you can customized in packer's json by "output_directory" param
if [ "$BUILD_TYPE"  == "vmware" ]; then
     VMDIR=output-${BUILD_TYPE}-vmx # output of Build VMWare from VMX
else
     VMDIR=output-${BUILD_TYPE}-ovf # output of Build VirtualBox from OVF
fi


VM_NAME=rackhd-${OS_VER}


CFG_FILE=template.cfg
# parameter file pass to packer
echo {                                                > $CFG_FILE
echo  \"playbook\": \"${ANSIBLE_PLAYBOOK}\",         >> $CFG_FILE
echo  \"vm_name\": \"${VM_NAME}\",                   >> $CFG_FILE
if [  -n "${DEBIAN_REPOSITORY}" ];  then
     echo  \"deb_repository\": \"${DEBIAN_REPOSITORY}\",  >> $CFG_FILE
fi
echo  \"rackhd_version\": \"${RACKHD_VERSION}\"      >> $CFG_FILE

if [[ $IS_UPLOAD_ATLAS == true ]]
then
    # NOTE: the ATLAS_TOKEN should be hiden in ENV Variable for security sake.
    echo ,                                           >> $CFG_FILE
    echo \"atlas_username\": \"${ATLAS_USERNAME}\" , >> $CFG_FILE
    echo \"atlas_name\": \"${ATLAS_NAME}\" ,         >> $CFG_FILE
    echo \"atlas_version\": \"${ATLAS_VERSION}\"     >> $CFG_FILE
fi
echo }                                               >> $CFG_FILE
# parameter file --- done


PACKER=packer
if [ -x /opt/packer/packer ]
then
    PACKER=/opt/packer/packer
fi



# Check Free Disk Space,  VMWare Workstation may stuck if disk space too small
if [ "$BUILD_TYPE"  == "vmware" ];  then
    fd_in_kb=$(df  .  | awk '/^\/dev/ {print $4}')
    fd_thres=$(expr 1024 '*' 1024 '*' 8)  # set to 8G as threshold.
    if [ $fd_in_kb -lt  $fd_thres ]
    then
        echo "The Free Up Disk Space($fd_in_kb KB) is not suffcient(recommended to $fd_thres KB). it may cause VMWare Workstation to stuck."
        exit 2
    fi
fi

# pre-process the packer template file
PACKER_TEMP=template-${OS_VER}.json.tmp
TMP_FILE_STREAM=$(cat template-${OS_VER}.json)
if [[ $IS_UPLOAD_ATLAS == false ]] && [[ "$BUILD_TYPE"  != "vmware" ]]
then
    if [ ! -n "$(which jq)" ] ; then
        echo "${ERROR_HEADER} jq is not installed . please install it, e.x. sudo apt-get install jq... Aborting. "
        exit 5
    fi
    # Delete the post-processors blocks in the template.json, before sending to 'packer build'
#    TMP_FILE_STREAM=$( jq 'del(."post-processors")'  template-${OS_VER}.json)
     TMP_FILE_STREAM=$( jq 'del(.["post-processors"][0][1])' template-${OS_VER}.json | jq 'del(.["push"])')

     echo "[Info] the template-${OS_VER}.json has been cutomized , to remove the vagrant-upload step"
     # Write back the template file
fi
echo "$TMP_FILE_STREAM" > $PACKER_TEMP

# indictor of packer build type for vmware
if [ "$BUILD_TYPE"  == "vmware" ];  then
      temp_src_format=vmx
else
      temp_src_format=ovf
fi


# execute 'packer build'
if [ "$BUILD_STAGE" == "BUILD_TEMPLATE" ]; then
    #Build from  iso, create a Pre-build Cache ( vmware:iso-->vmx, virtualbox: iso-->ovf )
    $PACKER build --force --only=${BUILD_TYPE}-iso  --var-file=$CFG_FILE  ${PACKER_TEMP}  | tee packer-install.log
else

    if [ "$BUILD_STAGE" == "BUILD_FINAL" ]; then
         #Build from cache template, and output the final image .(vmware: vmx-->ova, virtualbox: ovf-->box)
         $PACKER build --force --only=${BUILD_TYPE}-${temp_src_format}  --var-file=$CFG_FILE  --var "playbook=${ANSIBLE_PLAYBOOK}_mini" ${PACKER_TEMP}   | tee packer-install.log

    else
         #Build from scratch, vmware: iso-->vmx-->ova, virtualbox: iso-->ovf-->box
         $PACKER build --force --only=${BUILD_TYPE}-iso  --var-file=$CFG_FILE  ${PACKER_TEMP}  | tee packer-install.log && \
         $PACKER build --force --only=${BUILD_TYPE}-${temp_src_format}  --var-file=$CFG_FILE  --var "playbook=${ANSIBLE_PLAYBOOK}_mini" ${PACKER_TEMP}   | tee packer-install.log
    fi
fi

## Check packer build command status, because above we use pipeline, so use  ${PIPESTATUS[0]} to catch return-code of command before pipeline.(only works on bash)
if [ ${PIPESTATUS[0]} != 0 ]; then
    echo "${ERROR_HEADER} Packer Build failed.. exit"
    exit 3
fi

if  [ "$BUILD_STAGE" == "BUILD_TEMPLATE" ]; then
     if [ "$BUILD_TYPE"  == "vmware" ];  then
        echo "{INFO_HEADER} Build VMX is successful... Exit the HWIMO-BUILD Script. Use BUILD_STAGE=BUILD_FINAL and ANSIBLE_PLAYBOOK=rackhd_mini_xxxx env variable to build OVA from this VMX."
     else
        echo "{INFO_HEADER} Build OVF is successful... Exit the HWIMO-BUILD Script. Use BUILD_STAGE=BUILD_FINAL and ANSIBLE_PLAYBOOK=rackhd_mini_xxxx env variable to build Box from this OVF."
     fi
     exit 0
 fi

if [ "$BUILD_TYPE"  == "virtualbox" ];  then
    echo "${INFO_HEADER} Skip the post-processing(signing/clamscan/ovf-template injection) for virtualbox for the time being ..."
    mv packer_virtualbox-ovf_virtualbox.box ${VM_NAME}.box
    echo "****************"
    echo "Successfully create ${VM_NAME}.box"
    echo "****************"
    exit 0
fi


# Prepare Signing Key (used for Jenkins Build Release)
if [ -f "$CI_SIGNING_KEY" ]
then
    SIGN_ARGS="--privateKey=$CI_SIGNING_KEY"
    echo "${INFO_HEADER}Signing the OVA with the CI key"
else
    echo "${INFO_HEADER}No signing to be performed.. skip."
fi

BASENAME=${VM_NAME}
OVA="${BASENAME}.ova"

# Conver the VM Disk and VMX folder into an OVA
if [ "$BUILD_TYPE"  == "vmware" ];  then
    ovftool $SIGN_ARGS -o ${VMDIR}/${VM_NAME}.vmx $OVA
    if [ $? != 0 ]; then
        echo "${ERROR_HEADER} ovftool exec failed.. exit"
        exit 4
    fi
fi

# Do Virus Scan
if [ -x /usr/bin/clamscan ]
then
    echo "${INFO_HEADER} Doing ClamScan"
    rm -rf "$OVA.avscan"
    /usr/bin/clamscan --log="$OVA.avscan" --max-filesize=4000M --max-scansize=4000M -r ${VMDIR} --allmatch
else
    echo "${INFO_HEADER} skip clamscan..."
fi


if [ "${CUSTOMIZED_PROPERTY_OVA}" == "true" ];
then
    bash add_IP_property_to_ova.sh ${OVA}
    if [ $? != 0 ]; then
         echo "${ERROR_HEADER} add_IP_property_to_ova.sh ${OVA} failed! Abort!"
         exit 5
    fi
fi # End of [ "${CUSTOMIZED_PROPERTY_OVA}" == "true" ]


# Create MD5 & Sha256 checksum file

rm -f "$OVA.md5" "$OVA.sha"
md5sum "$OVA" > "$OVA.md5"
sha256sum  "$OVA" > "$OVA.sha"

echo "***************************"
echo "Created : $OVA"
echo "also find $OVA.md5 & $OVA.sha"
echo "***************************"


# Do gpg signing
if [ -f "$GPG_SIGNING_KEY" ]
then
    export GNUPGHOME=$(mktemp -d)
    trap -- "rm -rf $GNUPGHOME" EXIT
    gpg --allow-secret-key-import --import "$GPG_SIGNING_KEY"
    gpg -k

    rm -rf "$OVA.md5.gpg"
    gpg -a --output "$OVA.md5.gpg" --detach-sig "$OVA.md5"

    rm -rf "$OVA.sha.gpg"
    gpg -a --output "$OVA.sha.gpg" --detach-sig "$OVA.sha"

    if [ -f "$OVA.avscan" ]
    then
        gpg -a --output "$OVA.avscan.gpg" --detach-sig "$OVA.avscan"
    fi
fi

chmod a=r "$OVA"*


